# -*- coding: UTF-8 -*-
'''
@Project ：push_rk
@File ：week3code.py
@IDE ：PyCharm
@Author ：苦瓜
@Date ：2025/9/29 8:37
@Note: Something beautiful is about to happen !
'''
import matplotlib.pyplot as plt
import numpy as np
import torch
from sklearn.datasets import load_breast_cancer
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split

# 1.	数据集选择：
# 必须使用乳腺癌数据集进行模型的训练和测试。
# 数据集应包含患者的特征和对应的标签（恶性或良性）。
data, target = load_breast_cancer(return_X_y=True)
target = target.reshape(-1, 1)
print(" ====== 查看数据集 ====== ")
print(data.shape)
print(target.shape)
print(target)
print(data)

# 2.	数据预处理：
print("\n ====== 数据预处理 ====== ")
# 数据必须进行标准化处理，以确保模型训练的稳定性和效率。
# 标准化后的数据应转换为PyTorch张量，以便后续使用PyTorch进行模型训练。
scaler = StandardScaler()
x = scaler.fit_transform(data)
# 二分类问题 标签不需要标准化

# 转为torch张量
X = torch.tensor(x, dtype=torch.float)
Y = torch.tensor(target, dtype=torch.float)

# 切分数据集
X_train, X_test, y_train, y_test = train_test_split(X, Y, shuffle=False, random_state=42, test_size=0.2)
print(f"<X_train shape : {X_train.shape} | X_test shape : {X_test.shape}>")
print(f"<y_train shape : {y_train.shape} | y_test shape : {y_test.shape}>")


# 3.	模型定义：
# 定义一个简单的神经网络模型，用于对预处理后的乳腺癌数据集进行分类。
# 模型应包含一个线性层和一个Sigmoid激活函数，以适应二分类任务的需求。
# 输入特征数量应与乳腺癌数据集的特征数量相匹配。
class ClassifierCancer(torch.nn.Module):

    def __init__(self, *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)
        self.classifier = torch.nn.Sequential(
            torch.nn.Linear(in_features=X_train.shape[1], out_features=1),
            torch.nn.Sigmoid()
        )

    def forward(self, inputs):
        return self.classifier(inputs)


if __name__ == '__main__':
    model = ClassifierCancer()
    # 4.	损失函数和优化器：
    # 使用二进制交叉熵损失函数（BCELoss）作为模型的损失函数。
    # 选择RMSprop优化器来优化模型的参数。
    cost = torch.nn.BCELoss()
    optimizer = torch.optim.RMSprop(model.parameters(), lr=0.001)
    model.train()  # 训练，启动！
    # 5.	模型训练：
    # 通过迭代训练数据来训练模型，并定期输出损失值以监控训练过程。
    # 迭代次数可能需要调整以达到最佳的训练效果。
    print("\n ====== 模型训练 ======")
    losses = []  # 损失列表
    for epoch in range(2000):
        optimizer.zero_grad()  # 梯度置零
        y_pred_train = model(X_train)
        loss_ = cost(y_pred_train, y_train)
        losses.append(loss_.item())  # 保存损失
        loss_.backward()  # 计算梯度，梯度回传
        optimizer.step()  # 更新参数
        if epoch % 10 == 0:
            print(f"<Model Train {epoch} loss is ( {loss_} )>")

    # 6.	模型测试：
    # 使用测试集数据对训练好的模型进行评估。
    print("\n ====== 模型测试 ====== ")
    model.eval()  # 测试模式、启动！
    with torch.no_grad():
        y_pred_test = np.where(model(X_test)>0.5, 1, 0)
    print(y_pred_test)  # 输出预测的分类结果
    # 通过比较模型的预测结果和真实标签来计算分类的准确率。
    acc_ = (y_pred_test == y_test).float().mean()
    # 输出模型的准确率作为性能评估的指标。
    print(f"<Model Acc is ({acc_})>")

    # 绘制训练损失图
    plt.plot(losses, label='Loss', c='r')
    plt.title('Train Loss')
    plt.xlabel("Epoch")
    plt.ylabel("Loss")
    plt.legend()
    plt.show()

    # 7.	性能评估：
    # 准确率是评估模型性能的主要指标。
    # 可以通过调整模型结构、损失函数、优化器等来提高模型的准确率。
    # 8.	代码实现：
    # 代码应具有良好的可读性和可维护性。
    # 应使用适当的注释来解释代码的功能和逻辑。
    # 应遵循Python和PyTorch的编程规范和最佳实践。
    # 9.	结果输出：
    # 在训练过程中，应定期输出损失值以监控训练进度。
    # 在模型测试后，应输出模型的准确率作为性能评估的结果。
    # 可以在代码中添加适当的打印语句或使用matplotlib等库来可视化结果。
    # 10.	环境依赖：
    # 项目应明确列出所需的环境依赖，包括Python版本、PyTorch版本、sklearn版本等。
    # 应确保代码在指定的环境下能够正常运行并达到预期的效果。
